import math
import pandas as pd
import backtrader as bt
from pyLibs.Gadfly import StockStore, BackTraderStrategy as bts, BackTraderUtits as btu


class SingleCycleBollingerStrategy(bts.SingleCycleStrategy):
    def __init__(self, config):
        super().__init__()
        self.config = config.Strategy

    def next(self):
        for i, data in enumerate(self.datas):
            self._platform_break(i, data)

    def _platform_break(self, i, data):
        cond = self.condition[i]
        watch = None
        watch_last = self.watchList[data._name].tail(1)
        watch_status = watch_last['s当前状态'].values[0] if self.watchList[data._name].shape[0] > 0 else None
        if watch_status in [None, '关闭', '取消'] and cond['kCrossEmaMid'][0]:
            watch = {
                't时间日期': data.datetime.datetime(0),
                'p区间上轨': data.platform_upper[0],
                'p区间下轨': data.platform_lower[0],
            }
            if data.close[0] < data.platform_upper[0]:
                watch['s当前状态'] = '关注'
                watch['e说明解释'] = '一阳放量穿越均线和布林中轨，但未触及阶段高点！'
        if watch_status == '关注':
            watch = {
                't时间日期': data.datetime.datetime(0),
                'p区间上轨': data.platform_upper[0],
                'p区间下轨': data.platform_lower[0],
            }
            if data.high[0] < watch_last['p区间上轨'].values[0] and data.low[0] >= watch_last['p区间下轨'].values[0]:
                watch['s当前状态'] = '关注'
                watch['e说明解释'] = '继续在平台区间内整理，未形成突破形态'
            else:
                if data.close[0] > data.platform_upper[-1]:
                    watch['s当前状态'] = '买入'
                    watch['e说明解释'] = '突破整理平台，发出买入信号, 限价单：' + str(round(data.close[0], 3))
                    watch['p区间上轨'] = watch_last['p区间上轨'].values[0]     # 设定前期高点价
                    watch['p前期上轨'] = watch_last['p区间上轨'].values[0]
                    watch['l初始止损'] = data.platform_lower[0]      # 设定初始止损价
                    break_price = data.platform_upper[-1] + (data.platform_upper[-1] - data.platform_lower[0]) * 1.5
                    watch['l盈亏平衡'] = break_price    # 设定盈亏平衡价
                elif data.close[0] < watch_last['p区间下轨'].values[0]:
                    watch['s当前状态'] = '取消'
                    watch['e说明解释'] = '价格跌破整理区间下轨，取消关注'
                else:
                    watch['s当前状态'] = '关注'
                    watch['e说明解释'] = '继续在平台区间内部整理，未形成有效突破形态'
        if watch_status in ['买入', '等待', '回撤', '整理', '看空']:
            watch = {
                't时间日期': data.datetime.datetime(0),
                'p区间上轨': watch_last['p区间上轨'].values[0],
                'p区间下轨': watch_last['p区间下轨'].values[0],
                'p前期上轨': watch_last['p前期上轨'].values[0],
                'l初始止损': watch_last['l初始止损'].values[0],
                'l移动止损': watch_last['l移动止损'].values[0],
                'l盈亏平衡': watch_last['l盈亏平衡'].values[0],
            }
            if data.high[0] >= data.close[0] > watch_last['p区间上轨'].values[0]:
                watch['p区间上轨'] = data.high[0]
                if data.ratio[0] > 0:
                    watch['s当前状态'] = '等待'
                    if watch_last['s当前状态'].values[0] in ['买入', '整理']:
                        watch['e说明解释'] = '回撤后首根创出新高的K线'
                        watch['p前期上轨'] = watch_last['p区间上轨'].values[0]
                    else:
                        watch['e说明解释'] = '再次创出新高'
                        watch['p前期上轨'] = watch_last['p前期上轨'].values[0]
                else:
                    watch['s当前状态'] = '看空'
                    watch['e说明解释'] = '墓碑线或乌云压顶，看空'
            elif data.close[0] < watch_last['p区间上轨'].values[0] and data.ratio[0] > 0 \
                    and watch_last['s当前状态'].values[0] == '等待':
                watch['s当前状态'] = '等待'
                watch['e说明解释'] = '并未创出新高，但股价重心继续向上运动'
            elif data.high[0] > watch_last['p区间上轨'].values[0] > data.close[0]:
                watch['p区间上轨'] = data.high[0]
                k = {
                    '上影线': data.high[0] - data.close[0] if data.ratio > 0 else data.high[0] - data.open[0],
                    '下影线': data.open[0] - data.low[0] if data.ratio > 0 else data.close[0] - data.low[0],
                    '实体': data.close[0] - data.open[0] if data.ratio > 0 else data.open[0] - data.close[0],
                }
                if k['实体'] > k['上影线'] * 2 > k['下影线'] * 2:
                    watch['s当前状态'] = '卖出'
                    watch['e说明解释'] = '倍量 乌云盖顶 形态 卖出'
                elif k['上影线'] > k['实体'] * 2 and k['下影线'] <= k['上影线'] / 2:
                    watch['s当前状态'] = '关闭'
                    watch['e说明解释'] = '倍量 PinBar 形态 以上影线三分之一处限价卖出，价格：' + str(round(data.close + k['上影线'] / 3, 3))
                else:  # 倍量阳线
                    watch['s当前状态'] = '整理'
                    watch['e说明解释'] = '创新高后返回整理区间'
                    watch['p区间下轨'] = data.low[0]
                    if math.isnan(float(watch_last['l移动止损'].values[0])):
                        watch['l移动止损'] = watch_last['p前期上轨'].values[0]
                    else:
                        watch['l移动止损'] = watch_last['p区间下轨'].values[0]
            elif data.high[0] < watch_last['p区间上轨'].values[0]:
                if data.close[0] > watch_last['p前期上轨'].values[0]:
                    watch['p区间下轨'] = data.low[0] if data.low[0] < watch_last['p区间下轨'].values[0] else watch_last['p区间下轨'].values[0]
                    if data.high[-1] == watch_last['p区间上轨'].values[0]:
                        if data.ratio[-1] > 0:
                            '''if math.isnan(float(watch_last['移动止损'].values[0])):
                                watch['移动止损'] = watch_last['前期上轨'].values[0]
                            else:
                                watch['移动止损'] = watch_last['区间下轨'].values[0]'''
                            watch['p区间下轨'] = data.low[0]
                            watch['s当前状态'] = '整理'
                            watch['e说明解释'] = '进入回撤整理，移动止损设定为前期区间下轨'
                        else:
                            watch['s当前状态'] = '整理'
                            watch['e说明解释'] = '回调整理继续，判断统计回调整理的下轨'
                            pass
                    elif data.high[0] > watch_last['p区间上轨'].values[0]:
                        if data.ratio[0] > 0:
                            watch['s当前状态'] = '整理'
                            watch['e说明解释'] = '带创新高回落上影阳线的整理'
                            pass
                        else:
                            watch['s当前状态'] = '整理'
                            watch['e说明解释'] = '首次带创新高回落上影阴线的整理'
                            pass
                    else:
                        watch['s当前状态'] = '整理'
                        watch['e说明解释'] = '在本期高点和前期上轨之间的回撤或整理'
                        pass
                else:
                    watch['s当前状态'] = '看空'
                    watch['e说明解释'] = '跌破前期上轨'
                    pass

            '''
            if data.close[0] > watch_last['区间上轨'].values[0]:      # 创出新高
                watch['区间上轨'] = data.high[0]
                if data.close[0] > data.close[-1]:
                    watch['当前状态'] = '等待'
                    if math.isnan(float(watch_last['移动止损'].values[0])):
                        watch['移动止损'] = watch_last['前期上轨'].values[0]
                        watch['说明解释'] = '首创新高，最高价和收盘价均高于前K线，移动止损设定为前期上轨。'
                    else:
                        if watch_last['当前状态'].values[0] == '等待':
                            watch['移动止损'] = watch_last['移动止损'].values[0]
                            watch['说明解释'] = '再创新高，最高价和收盘价均高于前K线，移动止损保持不变'
                        if watch_last['当前状态'].values[0] == '回撤':
                            watch['前期上轨'] = watch_last['区间上轨'].values[0]
                            watch['移动止损'] = watch_last['区间下轨'].values[0]
                            watch['说明解释'] = '回撤后创新高，突破前期回撤平台，移动止损设定为前期区间下轨'
                else:
                    watch['当前状态'] = '回撤'
                    watch['区间下轨'] = data.low[0]
                    watch['说明解释'] = '开始回撤，最高高于前K但收盘低于前K，设定移动止损价位并记录回撤低点'
            else:
                if data.low[0] < watch_last['区间下轨'].values[0]:
                    watch['当前状态'] = '回撤'
                    watch['区间下轨'] = data.low[0]
                    watch['说明解释'] = '回撤阶段，最高价低于区间上轨、最低价均低于区间下轨，记录回撤低点'
                elif data.low[0] > watch_last['前期上轨'].values[0] and data.ratio[0] < 0:
                    if data.high[0] >= watch_last['区间上轨'].values[0]:
                        watch['当前状态'] = '回撤'
                        watch['区间上轨'] = data.high[0]
                        watch['区间下轨'] = data.low[0]
                        watch['说明解释'] = '开始回撤，w最高高于前K但收盘低于前K，设定移动止损价位并记录回撤低点'
                    elif data.high[-1] >= watch_last['区间上轨'].values[0]:
                        watch['当前状态'] = '回撤'
                        watch['区间上轨'] = data.high[0] if data.high[0] > watch_last['区间上轨'].values[0] else watch_last['区间上轨'].values[0]
                        watch['区间下轨'] = data.low[0]
                        watch['说明解释'] = '开始回撤，设定移动止损价位并记录回撤低点'
                    else:
                        watch['当前状态'] = '回撤'
                        watch['说明解释'] = '回撤阶段，最高价、最低价在整理区间范围内，此时不做任何记录和操作'
                else:
                    if data.high[0] > watch_last['区间上轨'].values[0] > data.close[0]:
                        watch['当前状态'] = '回撤'
                        watch['区间上轨'] = data.high[0]
                        watch['区间下轨'] = data.low[0]
                        watch['说明解释'] = '回撤异动，最高价创出新高，但收盘返回整理区间内，记录区间上轨'
                    else:
                        watch['当前状态'] = '回撤'
                        watch['说明解释'] = '回撤阶段，最高价最低价在整理区间范围内，此时不做任何记录和操作'
                
                if data.platform_upper[-1] > data.close[0] > watch_last['前期上轨'].values[0]:
                    # 收盘价低于昨日新高，大于前期上轨
                    up_line = data.high[0] - data.close[0] if data.ratio > 0 else data.high[0] - data.open[0]
                    down_line = data.open[0] - data.low[0] if data.ratio > 0 else data.close[0] - data.low[0]
                    entity_line = data.close[0] - data.open[0] if data.ratio > 0 else data.open[0] - data.close[0]
                    if cond['is_vol_multiplier']:   # 倍量
                        if data.ratio < 0:      # 阴线
                            if entity_line > up_line * 2 > down_line * 2 \
                                    and data.close[0] < data.close[-1] < data.open[0]:
                                watch['当前状态'] = '卖出'
                                watch['说明解释'] = '倍量 乌云盖顶 形态 卖出'
                            if up_line > entity_line * 2 and down_line <= entity_line:
                                watch['当前状态'] = '卖出'
                                watch['说明解释'] = '倍量 PinBar 形态 卖出'
                        else:       # 倍量阳线
                            watch['当前状态'] = '等待'
                            watch['说明解释'] = '倍量 有可能是明修栈道形态，买入衰竭现象，注意观察下一根K线'
                    else:
                        if data.low[0] > watch_last['区间下轨'].values[0]:
                            watch['区间下轨'] = data.low[0]
                            watch['当前状态'] = '等待'
                            watch['说明解释'] = '创出新高后，进入回撤状态，记录回撤的低点'
                        else:
                            watch['区间下轨'] = watch_last['platform_lower'].values[0]
                            watch['当前状态'] = '卖出'
                            watch['说明解释'] = '创出新高后，跌破前期平台低点（暂定）'
                if data.high[0] > watch_last['区间上轨'].values[0] > data.close[0] > watch_last['前期上轨'].values[0]:
                    watch['说明解释'] = '创出新高后，进入回撤状态，记录回撤的低点。'
                    if math.isnan(float(watch_last['移动止损'].values[0])):
                        watch['移动止损'] = watch_last['前期上轨'].values[0]
                        watch['说明解释'] += '首次创新高移动止损设定为前高。'
                    else:
                        watch['移动止损'] = watch_last['区间下轨'].values[0]
                        watch['说明解释'] += '后续新高移动止损设定为前低'
                    watch['区间下轨'] = data.low[0]
                    watch['当前状态'] = '等待'
            if data.high[0] < watch_last['区间下轨'].values[0] \
                    and data.close[0] > watch_last['前期上轨'].values[0]\
                    and data.low[0] < watch_last['区间下轨'].values[0]:
                watch['移动止损'] = watch_last['移动止损'].values[0]
                watch['区间下轨'] = data.low[0]
                watch['当前状态'] = '等待'
                watch['说明解释'] = '回撤状态，记录新的回撤的低点'
            if data.high[0] < watch_last['区间上轨'].values[0] \
                    and data.low[0] > watch_last['区间下轨'].values[0]:
                watch['移动止损'] = watch_last['移动止损'].values[0]
                watch['区间下轨'] = watch_last['区间下轨'].values[0]
                watch['当前状态'] = '等待'
                watch['说明解释'] = '平台震荡，未出现新的高点和低点，继续等待'
            if data.close[0] < watch_last['移动止损'].values[0]:
                watch['当前状态'] = '卖出'
                watch['说明解释'] = "跌破盈亏平衡价，卖出，卖出价格：￥" + str(round(data.close[0], 3))
                '''
        if watch is not None:
            self.watchList[data._name].loc[len(self.watchList[data._name])] = watch


class DoubleCycleBollingerStrategy(bts.DoubleCycleStrategy):
    def __init__(self, config):
        super().__init__()
        self.config = config.Strategy

    def next(self):
        for i, data in enumerate(self.datas):
            inds = self.indicators[i]
            cond = self.condition[i]
            watch = None
            watchLast = self.watchList[data._name].tail(1)
            if self.watchList[data._name].shape[0] > 0:
                watch_status = watchLast['status'].values[0]
                platform_upper_trend = watchLast['platform_upper'].values[0]
            else:
                watch_status = None
                platform_upper_trend = data.platform_upper_trend[0]
            """趋势周期中，MA13 > EMA34 and MID > EMA34 and EMA34 > MA(EMA34, 5)"""
            if data.volume_trend[0] > data.volume_ma_trend[0] and data.volume_trend[0] > data.volume_trend[-1] * 2 \
                    and data.close_trend[0] > data.ema_s_trend[0] and data.close_trend[0] > data.middle_trend[0] \
                    and watch_status in [None, 'Close', 'Cancel'] and data.ratio_trend[0] > 0:
                watch = {
                    'datetime': data.datetime.datetime(0),
                    'open': data.open[0],
                    'close': data.close[0],
                    'high': data.high[0],
                    'low': data.low[0],
                    'bollinger_upper': data.upper_trend[0],
                    'platform_upper': data.platform_upper_trend[0],
                    'platform_lower': data.platform_lower_trend[-1],
                    'stop_surplus': data.stop_surplus_trend[-1],
                }
                if data.close_trend[0] > data.platform_upper_trend[0] and data.close_trend[0] > data.upper_trend:
                    # 同时突破整理平台上沿和布林带上轨，价格创新高，买入
                    watch['status'] = 'Buy'
                    watch['explain'] = self.config.watch_status[1]
                elif data.platform_upper_trend[0] < data.close_trend[0] < data.upper_trend:
                    # 仅突破近期整理平台上沿，价格创新高，关注
                    watch['status'] = 'Watch'
                    watch['explain'] = self.config.watch_status[2]
                elif data.upper_trend < data.close_trend[0] < data.platform_upper_trend[0]:
                    # 仅向上突破布林带上轨，价格未创新高，关注
                    watch['status'] = 'Watch'
                    watch['explain'] = self.config.watch_status[3]
                else:
                    # 一阳放量穿多线，未突破整理平台上沿和布林带上轨，关注
                    watch['status'] = 'Watch'
                    watch['explain'] = self.config.watch_status[0]
            if watch_status == 'Watch':
                if data.high_trend[0] > watchLast['platform_upper'].values[0] > data.close_trend[0]:
                    # 价格继续创出近期新高，记录新平台高点
                    watch = {
                        'datetime': data.datetime.datetime(0),
                        'open': data.open[0],
                        'close': data.close[0],
                        'high': data.high[0],
                        'low': data.low[0],
                        'bollinger_upper': data.upper_trend[0],
                        'platform_upper': data.high_trend[0],
                        'platform_lower': data.platform_lower_trend[0],
                        'stop_surplus': data.stop_surplus_trend[0],
                        'status': 'Watch',
                        'explain': self.config.watch_status[4]
                    }
                if data.close[0] > watchLast['platform_upper'].values[0]:
                    # 同时突破整理平台上沿和布林带上轨，价格创新高，买入
                    watch = {
                        'datetime': data.datetime.datetime(0),
                        'open': data.open[0],
                        'close': data.close[0],
                        'high': data.high[0],
                        'low': data.low[0],
                        'bollinger_upper': data.upper_trend[0],
                        'platform_upper': data.high_trend[0],
                        'platform_lower': data.platform_lower_trend[-1],
                        'stop_surplus': data.stop_surplus_trend[0],
                        'status': 'Buy',
                        'explain': self.config.watch_status[1]
                    }
            if watch_status == 'Buy' and data.high_trend[0] > watchLast['platform_upper'].values[0] > data.close_trend[
                0]:
                # 仅突破近期整理平台上沿，价格创新高，买入
                watch = {
                    'datetime': data.datetime.datetime(0),
                    'open': data.open[0],
                    'close': data.close[0],
                    'high': data.high[0],
                    'low': data.low[0],
                    'bollinger_upper': data.upper_trend[0],
                    'platform_upper': data.high_trend[0],
                    'platform_lower': data.platform_lower_trend[0],
                    'stop_surplus': data.stop_surplus_trend[0],
                    'status': 'Waiting',
                    'explain': self.config.watch_status[4]
                }
            if watch_status in ['Watch', 'Buy'] and data.close[0] < watchLast['platform_upper'].values[0]:
                watch = {
                    'datetime': data.datetime.datetime(0),
                    'open': data.open[0],
                    'close': data.close[0],
                    'high': data.high[0],
                    'low': data.low[0],
                    'bollinger_upper': data.upper_trend[0],
                    'platform_upper': watchLast['platform_upper'].values[0],
                    'platform_lower': data.platform_lower_trend[0],
                    'stop_surplus': data.stop_surplus_trend[0],
                    'status': 'Cancel' if watch_status == 'Watch' else 'Close',
                    'explain': self.config.watch_status[6 if watch_status == 'Watch' else 7],
                }
            if watch is not None:
                self.watchList[data._name].loc[len(self.watchList[data._name])] = watch

    """
    *** 运用布林带宽指标来进行挤压突破系统实操 ***
    
    挤压突破系统Volatility Breakout System，是将布林带宽挤压的技巧引进，使得原本的挤压突破系统变得更容易操作。当布林带宽受到波动率下降的影响而
    缩小时，称为“挤压”，带宽受到波动率的驱使下，挤压完全来自于对这种波动率变化的纯粹反映。在这里，在带宽挤压下，如何识别整理区间的突破。
    
    - 当布林带宽处于低值时，波动率极小，此时突破前的挤压形成。
    - 当布林带宽处于高值时，波动率极大，此时为趋势反转的前兆。
    ![红色箭头为多单进场，绿色箭头为空单进场。](https://wx2.sinaimg.cn/large/005BwkZmgy1fybbrf2z3xj30jr070gn9.jpg)
    
    ★Head Fake★假跌破后真突破的假头
    > 实操过程来自于交易过程并非总是依照原本的常态进行，这其中最难的一种便是下图的Head Fake假跌破后真突破的假头，这种的困难只能借助于量能指标与波浪理论。这个复杂度太大，在没讲清楚量价与筹码分析，以及波浪理论之前，只能点到为止。这个是高阶中的高阶，顶级的技巧。
    - 当布林带宽处于低值时，波动率极小，此时突破前的挤压形成。
    - 当布林带宽处于高值时，波动率极大，此时为趋势反转的前兆。
    """
    def _patternVolatilityBreakout(self):
        pass
